package club.mrxiao.gps.socket;

import club.mrxiao.gps.common.factory.ServiceFactory;
import club.mrxiao.gps.domain.Entity;
import club.mrxiao.gps.socket.domain.GpsDataInputDTO;
import club.mrxiao.gps.socket.domain.GpsStateInputDto;
import club.mrxiao.gps.socket.domain.Gt06InputDTO;
import club.mrxiao.gps.socket.util.*;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * GSP数据处理
 * @author  xiaoyu
 *
 */
@Slf4j
public class GpsDataDispose implements Runnable {

	private static final String CONNECTION_RESET = "Connection reset";

	private static final String DATA_START = "7878";

	/**
	 * 输入流
	 */
	private DataInputStream dis;
	/**
	 * 输出流
	 */
	private DataOutputStream dos;
	private Boolean flag = true;
	/**
	 * 路线名
	 */
	private String name = null;
	/**
	 * 设备
	 */
	private Integer entityId = null;
	/**
	 * 设备imei码
	 */
	private String imei = null;

	private Gt06InputDTO gt06InputDTO;


	public GpsDataDispose(Socket socket){
		try {
			dis = new DataInputStream(socket.getInputStream());
			dos = new DataOutputStream(socket.getOutputStream());
		} catch (IOException e) {
			flag = false;
			CloseUtil.closeAll(dis,dos);
			GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
		}
		
	}

	@Override
	public void run() {
		while (flag) {
			if (gpsDataVerification(readFromClient())) {
				String protocolNumber = this.gt06InputDTO.getProtocolNumber();
				String content = this.gt06InputDTO.getContent();
				String seriesNumber = this.gt06InputDTO.getSeriesNumber();
				if (Gt06InputDTO.PROTOCOL_NUMBER_LOGIN_INFORMATION.equals(protocolNumber)) {
					String resultString = ProtocolUtil.serverResponseData(protocolNumber, seriesNumber);
					sendToClient(resultString);
					if (!equipmentLogin(content)){
						break;
					}
				}else if (Gt06InputDTO.PROTOCOL_NUMBER_GPSANDSTATE_INFORMATION.equals(protocolNumber)) {
					String resultString = ProtocolUtil.serverResponseData(protocolNumber, seriesNumber);
					gpsAndStateDataParse(resultString);
				}else if (Gt06InputDTO.PROTOCOL_NUMBER_STATE_INFORMATION.equals(protocolNumber)) {
					String resultString = ProtocolUtil.serverResponseData(protocolNumber, seriesNumber);
					stateDataParse(resultString);
				}else if (Gt06InputDTO.PROTOCOL_NUMBER_ICCID.equals(protocolNumber)) {
					log.info("ICCID信息：{},imei：{}", content,this.imei);
				}else if (Gt06InputDTO.PROTOCOL_NUMBER_GPS_INFORMATION.equals(protocolNumber)) {
					gpsDataParse();
				}else{
					log.info("未识别信息：{},imei：{}", content,this.imei);
				}
			}else{
				log.error("数据效验不通过,imei：{}",this.imei);
				break;
			}
		}
	}

	/**
	 * 设备GPS信息解析
	 */
	private void gpsDataParse(){
		if(isLogin()){
			String content = this.gt06InputDTO.getContent();
			log.info("设备GPS信息,imei:{},信息内容:{}",imei,content);
			try{
				GpsDataInputDTO gpsDataInputDTO = new GpsDataInputDTO(content,name,entityId,imei);
				if(gpsDataInputDTO.getLatitude() != null && gpsDataInputDTO.getLongitude() != null){
					ServiceFactory.newPoint(gpsDataInputDTO);
				}
			}catch (Exception e){
				log.error("设备GPS信息处理失败",e);
				GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
				flag = false;
				CloseUtil.closeAll(dis,dos);
			}
		}
	}

	/**
	 * 设备状态信息解析
	 */
	private void stateDataParse(String resultString){
		if(isLogin()){
			log.info("状态信息:{}",this.gt06InputDTO.getContent());
			GpsStateInputDto stateInputDto = new GpsStateInputDto(this.gt06InputDTO.getContent(),name,entityId,imei);
			ServiceFactory.updateState(stateInputDto);
			sendToClient(resultString);
		}
	}
	/**
	 * GPS、状态合并信息解析
	 */
	private void gpsAndStateDataParse(String resultString){
		if(isLogin()){
			String content = this.gt06InputDTO.getContent();
			log.info("GPS、状态合并信息:{}",content);
			try{
				String gpsData = content.substring(0, content.length() - 10);
				String sateData = content.substring(content.length() - 10, content.length());
				GpsDataInputDTO gpsDataInputDTO = new GpsDataInputDTO(gpsData,name,entityId,imei);
				if(gpsDataInputDTO.getLatitude() != null && gpsDataInputDTO.getLongitude() != null){
					ServiceFactory.newPoint(gpsDataInputDTO);
				}
				GpsStateInputDto stateInputDto = new GpsStateInputDto(sateData,name,entityId,imei);
				ServiceFactory.updateState(stateInputDto);
				sendToClient(resultString);
			}catch (Exception e){
				log.error("GPS、状态合并信息处理失败",e);
				GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
				flag = false;
				CloseUtil.closeAll(dis,dos);
			}
		}
	}

	/**
	 * 登录判断
	 * @return
	 */
	private Boolean isLogin(){
		if(StrUtil.isBlank(this.imei)){
			log.error("设备未登录，停止此socket服务");
			GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
			flag = false;
			CloseUtil.closeAll(dis,dos);
			return false;
		}
		return true;
	}

	/**
	 * gps 数据效验
	 * @param data
	 * @return
	 */
	private Boolean gpsDataVerification(String data){
		log.info("消息：{}",data);
		if(StrUtil.isBlank(data)){
			GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
			flag = false;
			CloseUtil.closeAll(dis,dos);
			return false;
		}
		String start = data.substring(0, 4);
		if(!DATA_START.equals(start)){
			return false;
		}
		try{
			this.gt06InputDTO = new Gt06InputDTO(data);
			// 协议号
			String protocolNumber = this.gt06InputDTO.getProtocolNumber();
			// 消息内容
			String content = this.gt06InputDTO.getContent();
			// 消息序列号
			String seriesNumber = this.gt06InputDTO.getSeriesNumber();
			String hexstr = String.format("%02x", this.gt06InputDTO.getLength()).toUpperCase()+ protocolNumber+ content+ seriesNumber;
			Crc16Util crc16 = new Crc16Util();
			crc16.reset();
			crc16.update(Byte2Hex.hexString2Bytes(hexstr));
			String crc = Integer.toHexString(crc16.getCrcValue()).toUpperCase();
			return this.gt06InputDTO.getCrc().equals(crc);
		}catch (Exception e){
			log.error("终端数据解析错误",e);
			return false;
		}
	}

	/**
	 * 设备登录
	 * @param imei
	 * @return
	 */
	private Boolean equipmentLogin(String imei){
		log.info("设备登录,imei:{}",imei);
		Entity entity = ServiceFactory.findEntityByImei(imei);
		if(entity == null){
			GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
			flag = false;
			CloseUtil.closeAll(dis,dos);
			log.error("未绑定设备，停止此socket服务");
			return false;
		}else{
			this.imei = imei;
			this.name = entity.getEntityName();
			this.entityId = entity.getEntityId();
			return true;
		}
	}

	/**
	 * 定义读取客户端数据的方法
	 * @return
	 */
	private String readFromClient() {
		String hex = "";
		try {
			byte[] bytes = new byte[1024];
			int size;
			size = dis.read(bytes);
			hex = Byte2Hex.bytesToHex(bytes, 0, size);
			return hex;
		} catch (IOException e) {
			if(CONNECTION_RESET.equals(e.getMessage())){
				log.info("{}连接断开",this.imei);
			}else{
				log.error("服务器接收客户端数据失败",e);
			}
			GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
			flag = false;
			CloseUtil.closeAll(dis,dos);
		}
		return hex;
	}


	/**
	 * 定义发送数据给客户端的方法
	 * @param resultString
	 */
	private void sendToClient(String resultString) {
		if(!StringUtil.isNullStr(resultString)){
			try {
				dos.write(Byte2Hex.hexString2Bytes(resultString));
				dos.flush();
			} catch (IOException e) {
				log.error("服务器发送数据给客户端失败",e);
				flag = false;
				CloseUtil.closeAll(dis,dos);
				GpsService.GPS_DATA_DISPOSE_LIST.remove(this);
			}
		}
	}
}
